package cn.tml.innermost.music.service.impl;

import cn.tml.innermost.framework.entity.enums.ResultCode;
import cn.tml.innermost.framework.exception.ServiceException;
import cn.tml.innermost.music.dos.*;
import cn.tml.innermost.music.vo.AlbumMusicVO;
import cn.tml.innermost.music.vo.AlbumInfoVO;
import cn.tml.innermost.music.params.AlbumParams;
import cn.tml.innermost.music.mapper.AlbumMapper;
import cn.tml.innermost.music.mapper.AlbumMusicMapper;
import cn.tml.innermost.music.service.AlbumService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 燧枫
 * @since 2022-10-18
 */
@Service
public class AlbumServiceImpl extends ServiceImpl<AlbumMapper, Album> implements AlbumService {

    @Resource
    private AlbumMusicMapper albumMusicMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    // 专辑的redsi前缀
    private static final String MUSIC_ALBUM = "music-album:";

    /**
     * 获取专辑信息
     *
     * @param albumId
     * @return AlbumInfoVO
     */
    @Override
    public AlbumInfoVO getAlbumInfo(Long albumId) {
        Album album = null;
        if (redisTemplate.hasKey(MUSIC_ALBUM + albumId)) {
//        如果在redis中, 直接得到歌单实体类
            album = (Album) redisTemplate.opsForValue().get(MUSIC_ALBUM + albumId);
        } else {
//        根据歌单id得到专辑实体类
            album = this.getById(albumId);
//        如果查不到，直接抛异常
            if (album == null)
                throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        如果删除标记为true，直接抛异常
            if (album.getDeleteFlag() == true)
                throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        保存至redis中
            redisTemplate.opsForValue().set(MUSIC_ALBUM + albumId, album, 60 * 60, TimeUnit.SECONDS);
        }
//        如果有，转为AlbumInfoVO并返回
        return AlbumInfoVO.valueOf(album);
    }

    /**
     * 获取专辑所有歌曲
     *
     * @param albumId
     * @return AlbumMusicVO
     */
    @Override
    public AlbumMusicVO getAlbumAllMusicById(Long albumId) {
        AlbumMusicVO albumMusicVO = new AlbumMusicVO();
        if (redisTemplate.hasKey(MUSIC_ALBUM + albumId + "AllMusic")) {
//        如果在redis中, 直接得到歌单实体类
            albumMusicVO = (AlbumMusicVO) redisTemplate.opsForValue().get(MUSIC_ALBUM + albumId + "AllMusic");
        } else {
//        根据专辑id得到专辑实体类
            Album album = this.getById(albumId);
//        如果查不到，直接抛异常
            if (album == null)
                throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        如果删除标记为true，直接抛异常
            if (album.getDeleteFlag() == true)
                throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        从关系表中查出所有所有实体类
            Map<String, Object> columnMap = new HashMap<>();
            columnMap.put("album_id", albumId);
            albumMusicVO.setAlbumMusics(albumMusicMapper.selectByMap(columnMap));
            albumMusicVO.setAlbumInfoVO(AlbumInfoVO.valueOf(album));
//        保存至redis中
            redisTemplate.opsForValue().set(MUSIC_ALBUM + albumId + "AllMusic", albumMusicVO, 60 * 60, TimeUnit.SECONDS);
        }
//        返回musicAlbumVO
        return albumMusicVO;
    }


    /***
     * @description: 新建一个专辑
     * @param albumParams
     * @return: AlbumInfoVO
     */
    @Override
    public AlbumInfoVO addAlbum(AlbumParams albumParams) {
//        先将albumParams转为实体类
        Album album = Album.valueOf(albumParams);
//        将实体类插入数据库中，如果失败，抛异常
        if (!this.save(album))
            throw new ServiceException(ResultCode.ALBUM_SAVE_FAIL);
//        如果成功，初始化参数，转为MusicListInfoVO并返回
        album.setMusicNums(0);
        album.setPlaysNums(0);
        album.setCollectionNums(0);
//        保存至redis中
        redisTemplate.opsForValue().set(MUSIC_ALBUM + album.getId(), album, 60 * 60, TimeUnit.SECONDS);
        return AlbumInfoVO.valueOf(album);
    }

    /***
     * @description: 删除一个专辑
     * @param albumId
     * @return: AlbumInfoVO
     */
    @Override
    public AlbumInfoVO delAlbum(Long albumId) {
//        根据专辑id得到专辑实体类
        Album album = this.getById(albumId);
//        如果查不到，直接抛异常
        if (album == null)
            throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        将删除标记至为true，并更新至数据库中，失败，抛异常
        album.setDeleteFlag(true);
        if (!updateById(album))
            throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
//        将redsi中的album对象直接删除
        if (redisTemplate.hasKey(MUSIC_ALBUM + albumId)) {
            if (!redisTemplate.delete(MUSIC_ALBUM + albumId))
                throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
        }
        if (redisTemplate.hasKey(MUSIC_ALBUM + albumId + "AllMusic")) {
            if (!redisTemplate.delete(MUSIC_ALBUM + albumId + "AllMusic"))
                throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
        }
//        如果成功，转为AlbumInfoVO并返回
        return AlbumInfoVO.valueOf(album);
    }

    /***
     * 修改歌单信息
     * @param albumId
     * @param albumParams
     * @return: AlbumInfoVO
     */
    @Override
    public AlbumInfoVO updateAlbum(Long albumId, AlbumParams albumParams) {
//        根据专辑id得到专辑实体类
        Album album = this.getById(albumId);
//        如果查不到，直接抛异常
        if (album == null)
            throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        如果删除标记为true，直接抛异常
        if (album.getDeleteFlag() == true)
            throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        将实体类同步至AlbumParams，并更新至数据库中
        album.setId(albumId);
        album.setName(albumParams.getName());
        album.setIssueTime(albumParams.getIssueTime());
        album.setLanguages(albumParams.getLanguages());
        album.setGenres(albumParams.getGenres());
        album.setIssueCompany(albumParams.getIssueCompany());
        album.setCoverUrl(albumParams.getCoverUrl());
        album.setDescription(albumParams.getDescription());
        if (!updateById(album))
            throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
//        查看是否在redis中
        if (redisTemplate.hasKey(MUSIC_ALBUM + albumId)) {
//        如果在redis中, 先将将redsi中的album对象删除
            if (!redisTemplate.delete(MUSIC_ALBUM + albumId))
                throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
//        再将新的对象放进redis中
            redisTemplate.opsForValue().set(MUSIC_ALBUM + albumId, album, 60 * 60, TimeUnit.SECONDS);
        }
        if (redisTemplate.hasKey(MUSIC_ALBUM + albumId + "AllMusic")) {
            if (!redisTemplate.delete(MUSIC_ALBUM + albumId + "AllMusic"))
                throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
        }
//        如果成功，转为AlbumInfoVO并返回
        return AlbumInfoVO.valueOf(album);
    }

    /***
     * 添加歌曲到专辑
     * @param albumMusics
     * @return: AlbumInfoVO
     */
    @Override
    public AlbumInfoVO addMusicToAlbum(List<AlbumMusic> albumMusics) {
//        将之前的redis缓存直接删掉即可
        if (redisTemplate.hasKey(MUSIC_ALBUM + albumMusics.get(0).getAlbumId() + "AllMusic")) {
            redisTemplate.delete(MUSIC_ALBUM + albumMusics.get(0).getAlbumId() + "AllMusic");
        }
//        根据专辑id获取专辑实体类
        Album album = this.getById(albumMusics.get(0).getAlbumId());
//        如果查不到，直接抛异常
        if (album == null)
            throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        直接将整个歌曲list添加至关系表中
        int addSuccessNums = 0;
        for (int i = 0; i < albumMusics.size(); i++) {
            int isAddSuccess = albumMusicMapper.
                    insert(albumMusics.get(i));
            if (isAddSuccess == 1) addSuccessNums++;
        }
//        专辑实体类的歌曲数量加上新增的歌曲数量
        album.setMusicNums(album.getMusicNums() + albumMusics.size());
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(album))
            throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
//        如果成功，转为AlbumInfoVO并返回
        return AlbumInfoVO.valueOf(album);
    }

    /***
     * 删除专辑中的歌曲
     * @param albumMusics
     * @return: AlbumInfoVO
     */
    @Override
    public AlbumInfoVO delMusicToAlbum(List<AlbumMusic> albumMusics) {
//        将之前的redis缓存直接删掉即可
        if (redisTemplate.hasKey(MUSIC_ALBUM + albumMusics.get(0).getAlbumId() + "AllMusic")) {
            redisTemplate.delete(MUSIC_ALBUM + albumMusics.get(0).getAlbumId() + "AllMusic");
        }
//        根据专辑id获取专辑实体类
        Album album = this.getById(albumMusics.get(0).getAlbumId());
//        如果查不到，直接抛异常
        if (album == null)
            throw new ServiceException(ResultCode.ALBUM_GET_FAIL);
//        直接将整个歌曲list在关系表中删除
        Long albumId = albumMusics.get(0).getAlbumId();
        int delSuccessNums = 0;
        for (int i = 0; i < albumMusics.size(); i++) {
            Map<String, Object> columnMap = new HashMap<>();
            columnMap.put("album_id", albumId);
            columnMap.put("music_id", albumMusics.get(i).getMusicId());
            int delSuccess = albumMusicMapper.deleteByMap(columnMap);
            if (delSuccess == 1) delSuccessNums++;
        }
//        专辑实体类的歌曲数量减去删除的歌曲数量
        album.setMusicNums(album.getMusicNums() - delSuccessNums);
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(album))
            throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
//        如果成功，转为AlbumInfoVO并返回
        return AlbumInfoVO.valueOf(album);
    }


    public List<AlbumInfoVO> getRandomAlbumsFromTop1000(int count) {
        try {
            if (redisTemplate.hasKey(MUSIC_ALBUM + "top1000Albums")) {
                Set<String> randomAlbumJsonSet = redisTemplate.opsForSet().distinctRandomMembers(MUSIC_ALBUM + "top1000Albums", count);
                List<AlbumInfoVO> randomAlbumList = new ArrayList<>();
                ObjectMapper objectMapper = new ObjectMapper();
                for (String albumJson : randomAlbumJsonSet) {
                    AlbumInfoVO albumInfoVO = objectMapper.readValue(albumJson, AlbumInfoVO.class);
                    randomAlbumList.add(albumInfoVO);
                }
                return randomAlbumList;
            } else {
                addTop1000AlbumsToRedis();
                return getRandomAlbumsFromTop1000(count);
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new ServiceException(ResultCode.ALBUM_TOP1000_FAIL);
        }
    }

    public void addTop1000AlbumsToRedis() throws JsonProcessingException {
        List<Album> top1000Albums = this.baseMapper.getTop1000ByIssueTime();
        for (Album album : top1000Albums) {
            AlbumInfoVO albumInfoVO = AlbumInfoVO.valueOf(album);
            String albumJson = new ObjectMapper().writeValueAsString(albumInfoVO);
            redisTemplate.opsForSet().add(MUSIC_ALBUM + "top1000Albums", albumJson);
        }
    }
}
